/*
 * AbleBaker.java
 *
 * Created on September 16, 2007, 3:01 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package ablebaker;


import java.util.ArrayList;
import java.util.Random;
import jsim.statistic.Histogram;
import jsim.statistic.SampleStat;
import jsim.variate.Exponential;
import simulationengine.event.Entity;
import simulationengine.event.Event;
import simulationengine.event.Scheduler;

/**
 * This class simulates the able baker call center as described in example 3.1
 * @author edrucker
 */
public class AbleBaker
{
	static final int SIM_LENGTH = 10000;
	
	static boolean aBusy;
	static boolean bBusy;
	static int numCallsWaiting;
	
	static int numCallsCompleted;
	
	static Scheduler scheduler;
	
	static SampleStat systemStats; 
	static SampleStat waitStats;
	static SampleStat serviceStats;
	
	static ArrayList<Entity> waitQueue = new ArrayList<Entity>();
	
    
	/** Creates a new instance of AbleBaker */
	public AbleBaker() 
	{
		numCallsWaiting = 0;
		aBusy = false;
		bBusy = false;

		scheduler = new Scheduler();
		
		systemStats = new SampleStat("Time in system");
		waitStats = new SampleStat("Time in wait queue");
		serviceStats = new SampleStat("Time in service");
	}
    
	static class Arrival extends Event
	{
		Arrival(Entity call, double time)
		{
			super(call, time); 
		}

		public void occur()
		{
			//schedule next arrival
			Random rand = new Random();
			double interArrivalTime = scheduler.getSystemTime();
			int randInt = rand.nextInt(100);
			if(randInt < 25)
				interArrivalTime += 1.0;
			else if (randInt < 65)
				interArrivalTime += 2.0;
			else if (randInt < 85)
				interArrivalTime += 3.0;
			else 
				interArrivalTime += 4.0;
			scheduler.schedule(new Arrival(new Entity(), interArrivalTime));
			
			//if Able or Baker is free, jump into service, otherwise jump into wait queue
			getEntity().setStartTime(scheduler.getSystemTime());
			if(!aBusy)
			{
				waitStats.tally(0.0);
				aBusy = true;
				
				double ableFinishTime = scheduler.getSystemTime();
				//determine amount of time
				randInt = rand.nextInt(100);
				if(randInt < 30)
					ableFinishTime += 2.0;
				else if(randInt < 58)
					ableFinishTime += 3.0;
				else if(randInt < 83)
					ableFinishTime += 4.0;
				else
					ableFinishTime += 5.0;
				serviceStats.tally(ableFinishTime - scheduler.getSystemTime());
				
				scheduler.schedule(new AbleServiceCompletion(getEntity(), ableFinishTime));
			}
			
			else if (!bBusy)
			{
				waitStats.tally(0.0);
				bBusy = true;
				
				double bakerFinishTime = scheduler.getSystemTime();
				//determine amount of time
				randInt = rand.nextInt(100);
				if(randInt < 35)
					bakerFinishTime += 3.0;
				else if(randInt < 60)
					bakerFinishTime += 4.0;
				else if(randInt < 80)
					bakerFinishTime += 5.0;
				else
					bakerFinishTime += 6.0;
				serviceStats.tally(bakerFinishTime - scheduler.getSystemTime());
				
				scheduler.schedule(new BakerServiceCompletion(getEntity(), bakerFinishTime));
			}
			
			else
			{
				waitQueue.add(getEntity());
			}
		}
	}
    
	static class AbleServiceCompletion extends Event
	{
		AbleServiceCompletion(Entity call, double time)
		{
			super(call, time);
		}

		public void occur()
		{
			aBusy = false;
			numCallsCompleted++;
			
			//for stat purposes
	//		timesInService.add(new Double(scheduler.getSystemTime() - getEntity().getStartTime()));
			systemStats.tally(scheduler.getSystemTime() - getEntity().getStartTime());
			
			if(!waitQueue.isEmpty())
			{
				aBusy = true;
				
				Random rand = new Random();
				
				Entity nextEntity = waitQueue.remove(0);
				waitStats.tally(scheduler.getSystemTime() - nextEntity.getStartTime());
				
				double ableFinishTime = scheduler.getSystemTime();
				//determine amount of time
				int randInt = rand.nextInt(100);
				if(randInt < 30)
					ableFinishTime += 2.0;
				else if(randInt < 58)
					ableFinishTime += 3.0;
				else if(randInt < 83)
					ableFinishTime += 4.0;
				else
					ableFinishTime += 5.0;
				serviceStats.tally(ableFinishTime - scheduler.getSystemTime());
				
				scheduler.schedule(new AbleServiceCompletion(nextEntity, ableFinishTime));
			}
		}
	}
	
	static class BakerServiceCompletion extends Event
	{
		BakerServiceCompletion(Entity call, double time)
		{
			super(call, time);
		}

		public void occur()
		{
			bBusy = false;
			numCallsCompleted++;
		
			//for stat purposes
	//		//timesInService.add(new Double(scheduler.getSystemTime() - getEntity().getStartTime()));
			systemStats.tally(scheduler.getSystemTime() - getEntity().getStartTime());
			
			if(!waitQueue.isEmpty())
			{
				bBusy = true;
				
				Random rand = new Random();
				
				Entity nextEntity = waitQueue.remove(0);
				waitStats.tally(scheduler.getSystemTime() - nextEntity.getStartTime());
				
				double bakerFinishTime = scheduler.getSystemTime();
				//determine amount of time
				int randInt = rand.nextInt(100);
				if(randInt < 30)
					bakerFinishTime += 2.0;
				else if(randInt < 58)
					bakerFinishTime += 3.0;
				else if(randInt < 83)
					bakerFinishTime += 4.0;
				else
					bakerFinishTime += 5.0;
				serviceStats.tally(bakerFinishTime - scheduler.getSystemTime());
				
				scheduler.schedule(new BakerServiceCompletion(nextEntity, bakerFinishTime));
			}
		}
	}
	
    /**
     * This method starts the simulation and continues until finished
     */
	public static void run()
	{
		scheduler.schedule(new Arrival(new Entity(), 0.0));
		
		while(numCallsCompleted < SIM_LENGTH)
		{
			scheduler.handle();
		}
		
		
		systemStats.printStat();
		sleep();
		waitStats.printStat();
		sleep();
		serviceStats.printStat();
		
	}
	
	private static void sleep()
	{
		for (int i = 0; i < 10000; i++)
		{
			for(int j = 0; j < 10000; j++)
			{
				j = j;
			}
		}
	}

	
	
    /**
     * Creates a new AbleBaker simulation and begins
     */
	public static void main(String args[])
	{
		AbleBaker ab = new AbleBaker();
		ab.run();

	}
}
